/*
 * MultiRtpMediaChannel.cpp
 *
 *  Created on: 2018年5月1日
 *      Author: zhengboyuan
 */

#include "MultiRtpMediaChannel.h"

#include "UdpRtpSocket.h"
#include "TcpcRtpSocket.h"
#include "TcpsRtpSocket.h"
#include "RtcpRtpSocket.h"

#include "RtpHeader.h"
#include "RtpConst.h"

#include "RtpUrl.h"
#include "CodecName.h"
#include "CLog.h"

#include "RtpPackerFactory.h"


namespace av
{


class MultiRtpRecvThread : public comn::Thread
{
public:
	MultiRtpMediaChannel* m_channel;

	explicit MultiRtpRecvThread(MultiRtpMediaChannel* channel):
		m_channel(channel)
	{
	}

	~MultiRtpRecvThread()
	{
	    if (isRunning())
	    {
	        stop();
	    }
	}

	virtual int run()
	{
		while (!m_canExit)
		{
			m_channel->handleRecv();
		}
		return 0;
	}

	virtual void doStop()
	{
		m_channel->cancelRecv();
	}

};



MultiRtpMediaChannel::MultiRtpMediaChannel(RtpProtocol protocol):
		m_protocol(protocol),
		m_streamMode(RtpMedium::kSendRecv),
		m_payload(RtpConst::DYNAMIC_PAYLOAD),
		m_ssrc(),
		m_maxPktSize(RtpConst::RTP_PACKET_SIZE),
		m_formatReady()
{
	m_socket.reset(createSocket(m_protocol));
	m_recvThread.reset(new MultiRtpRecvThread(this));

	CLog::debug("MultiRtpMediaChannel. protocol: %d\n", protocol);
}

MultiRtpMediaChannel::~MultiRtpMediaChannel()
{
	close();
}

int MultiRtpMediaChannel::open(const std::string& url, const std::string& params)
{
	std::string ip;
	int port = 0;
	RtpUrl::parse(url, ip, port);

	if (!m_socket->open(ip.c_str(), port))
	{
		return ENOENT;
	}

	return 0;
}

void MultiRtpMediaChannel::close()
{
	stopStream();

	if (m_socket)
	{
	    m_socket->close();
	}
}

bool MultiRtpMediaChannel::isOpen()
{
	return m_socket->isOpen();
}

int MultiRtpMediaChannel::startStream()
{
	setState(STATE_PLAYING);

	if ((m_streamMode & RtpMedium::kSendOnly) != 0)
	{
		if (!isRunning())
		{
			start();
		}
	}
	else if ((m_streamMode & RtpMedium::kRecvOnly) != 0)
	{
		if (!m_recvThread->isRunning())
		{
			m_recvThread->start();
		}
	}

	return 0;
}

int MultiRtpMediaChannel::pauseStream()
{
	return 0;
}

void MultiRtpMediaChannel::stopStream()
{

	setState(STATE_STOPPED);

	if (m_recvThread)
	{
	    if (m_recvThread->isRunning())
	    {
	        m_recvThread->stop();
	    }

	    m_recvThread.reset();
	}

	if (isRunning())
	{
		stop();
	}
}

int MultiRtpMediaChannel::forceKeyFrame()
{
	return 0;
}


void MultiRtpMediaChannel::onMediaFormat(const MediaFormat& fmt)
{
}

void MultiRtpMediaChannel::onMediaPacket(MediaPacketPtr& pkt)
{
	if (pkt->size <= 0)
	{
		return;
	}

	int queSize = (int)m_pktQueue.push(pkt);
	if (queSize > (RtpConst::QUEUE_MAX_SIZE * 2))
	{
		m_pktQueue.clear();
	}
	else if (queSize > RtpConst::QUEUE_MAX_SIZE)
	{
		m_pktQueue.dropUntilKeyFrame();
	}
}

void MultiRtpMediaChannel::onMediaEvent(int event)
{
	// pass
}


NetAddress MultiRtpMediaChannel::getLocalAddr()
{
	return m_socket->getLocalAddr();
}

void MultiRtpMediaChannel::setMaxPacetSize(size_t size)
{
	if (size > RtpConst::RTP_MIN_SIZE)
	{
		m_maxPktSize = size;
	}
}

void MultiRtpMediaChannel::setPayload(uint8_t payload)
{
	m_payload = payload;
}

uint8_t MultiRtpMediaChannel::getPayload()
{
	return m_payload;
}

void MultiRtpMediaChannel::setSsrc(uint32_t ssrc)
{
	m_ssrc = ssrc;
	m_socket->setSsrc(ssrc);
}

uint32_t MultiRtpMediaChannel::getSsrc()
{
	return m_ssrc;
}

void MultiRtpMediaChannel::setStreamMode(int mode)
{
	m_streamMode = mode;
}

int MultiRtpMediaChannel::getStreamMode()
{
	return m_streamMode;
}

void MultiRtpMediaChannel::setPeerAddr(const std::string& ip, int port)
{
	m_peerAddress.set(ip, port);

	m_socket->setPeer(ip.c_str(), port);
}

void MultiRtpMediaChannel::setPeerMedium(const std::string& mediaType, int payload, const std::string& codec, int clockrate, const std::string& sprop)
{
	setupPacker(mediaType, payload, codec);
}

NetAddress MultiRtpMediaChannel::getPeerAddr()
{
	return m_peerAddress;
}

RtpSocket* MultiRtpMediaChannel::createSocket(RtpProtocol protocol)
{
	if (protocol == kTcpClient)
	{
		return new TcpcRtpSocket();
	}
	else if (protocol == kTcpServer)
	{
		return new TcpsRtpSocket();
	}
	else if (protocol == kRtcp)
	{
		return new RtcpRtpSocket();
        //return new UdpRtpSocket();
	}
	else
	{
		return new UdpRtpSocket();
	}
}

int MultiRtpMediaChannel::run()
{
	while (!m_canExit)
	{
        MediaPacketPtr pkt;
        if (!m_pktQueue.pop(pkt, -1))
        {
            continue;
        }

        sendPacket(pkt);
	}
	return 0;
}

void MultiRtpMediaChannel::doStop()
{
	m_pktQueue.cancelWait();
}

void MultiRtpMediaChannel::sendPacket(MediaPacketPtr& pkt)
{
	if (!m_rtpPacker)
	{
		return;
	}

	RtpPackerSink sink = std::bind(&MultiRtpMediaChannel::sendRtpPacket, this,
		std::placeholders::_1);
	m_rtpPacker->pack(*pkt, m_maxPktSize, sink);
}

void MultiRtpMediaChannel::setupPacker(const std::string& mediaType, int payload, const std::string& codec)
{
	int mcodec = CodecName::parse(codec);
	MediaType mtype = (mediaType == RtpMedium::VIDEO) ? av::MEDIA_TYPE_VIDEO : av::MEDIA_TYPE_AUDIO;

	m_rtpPacker.reset(RtpPackerFactory::createPacker(mtype, codec));
	m_rtpUnPacker.reset(RtpPackerFactory::createUnPacker(mtype, codec));

	m_rtpPacker->setPayload(payload);
	m_rtpPacker->open();

	m_rtpUnPacker->open(mtype, mcodec);

}

void MultiRtpMediaChannel::handleRecv()
{
	RtpPacketHandler handler = std::bind(&MultiRtpMediaChannel::handleRecvRtpPacket, this,
		std::placeholders::_1);

	m_socket->recv(handler, 1000 * 3);
}

void MultiRtpMediaChannel::cancelRecv()
{
    if (m_socket)
    {
        m_socket->abort();
    }

}

void MultiRtpMediaChannel::onRecvPacket(MediaPacket& packet)
{
	if (!m_formatReady)
	{
		if (m_rtpUnPacker->getFormat(m_format))
		{
			m_formatReady = true;

			fireMediaFormat(m_format);
		}
	}

    av::MediaPacketPtr pkt(new av::MediaPacket(), ffmpeg::PacketDeleter());
    av_packet_ref(pkt.get(), &packet);

    fireMediaPacket(pkt);
}

void MultiRtpMediaChannel::sendRtpPacket(RtpPacket& packet)
{
    int tryCount = TRY_COUNT;
    while (getState() == STATE_PLAYING && tryCount > 0)
    {
        if (m_socket->send(packet, DEFAULT_TIMEOUT))
        {
            break;
        }

        tryCount--;
    }
}

void MultiRtpMediaChannel::handleRecvRtpPacket(RtpPacket& packet)
{
    av::MediaPacketPtr pktOut(new av::MediaPacket(), ffmpeg::PacketDeleter());
	if (m_rtpUnPacker->unpack(packet, *pktOut))
	{
		onRecvPacket(*pktOut);
	}
}


} /* namespace av */
